/**
 * This file is part of Idegen, the application to create IDE project files.
 *
 * @copyright Copyright (c) 2011-2012, Walter Tamboer http://waltertamboer.nl
 * @file Idegen/IO/WinFileSystem.cpp
 */

#include "Idegen/PrecompiledHeader.hpp"
#include "Idegen/IO/Directory.hpp"
#include "Idegen/IO/File.hpp"
#include "Idegen/IO/FileSystem.hpp"

namespace Idegen
{
	std::string AbsoluteToRelativePath( const std::string& path )
	{
		return AbsoluteToRelativePath( path, GetStartupDirectory() );
	}

	std::string AbsoluteToRelativePath( const std::string& path, const std::string& base )
	{
		std::string result = base;
		if ( !path.empty() )
		{
			const std::string workingBase = AppendSystemSlash( base );
			const std::string workingPath = AppendSystemSlash( path );

			const StringList baseList = SplitPath( workingBase );
			const StringList pathList = SplitPath( workingPath );

			size_t leftOvers = 0;
			std::string basePath;

			for ( size_t i = 0; i < baseList.size(); ++i )
			{
				if ( i < pathList.size() )
				{
					if ( baseList[ i ] != pathList[ i ] )
					{
						leftOvers = baseList.size() - i;
						break;
					}
				
					basePath += baseList[ i ] + '\\';
				}
				else
				{
					break;
				}
			}

			result = "";
			for ( size_t i = 0; i < leftOvers; ++i )
			{
				result.append( ".." );
				result.append( "\\" );
			}

			result += basePath == workingPath ? "" : path.substr( basePath.size() );
		}
		return result;
	}
	
	std::string AppendSystemSlash( const std::string& path )
	{
		std::string result = ConvertToSystemSlashes( path );

		if ( result.size() > 0 )
		{
			if ( result[ result.size() - 1 ] != '\\' )
			{
				result += TEXT('\\');
			}
		}
		else
		{
			result = TEXT('\\');
		}

		return result;
	}

	void CreateDirectory( const std::string& path )
	{
		StringList pathList = SplitPath( path );

		std::string fullPath;

		for ( StringList::iterator it = pathList.begin(); it != pathList.end(); ++it )
		{
			fullPath += (*it) + '\\';

			::CreateDirectoryA( fullPath.c_str(), NULL );
		}
	}
	
	std::string ConvertToSystemSlashes( const std::string& path )
	{
		std::string result = path;

		for ( std::string::iterator it = result.begin(); it != result.end(); ++it )
		{
			if ( (*it) == TEXT('/') )
			{
				(*it) = TEXT('\\');
			}
		}

		return result;
	}

	
	bool FileExists( const std::string& path )
	{
		// @todo
		path;
		return true;
	}
	
	DirectoryList GetDirectories( const std::string& path )
	{
		DirectoryList result;
		WIN32_FIND_DATAA findFileData;

		const std::string fullPath = path + "\\*.*";
		HANDLE hFind = FindFirstFileA( fullPath.c_str(), &findFileData );

		if ( hFind != INVALID_HANDLE_VALUE )
		{
			do
			{
				const std::string fileName = findFileData.cFileName;

				if ( fileName != "." && fileName != ".." )
				{
					if ( !( findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) )
					{
						const std::string itemPath = path + "\\" + fileName;

						if ( findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
						{
							result.push_back( Directory( itemPath ) );
						}
					}
				}

			} while ( FindNextFileA( hFind, &findFileData ) );

			FindClose( hFind );
		}

		return result;
	}

	std::string GetDirectoryPath( const std::string path )
	{
		const StringList splitted = SplitPath( path );
		std::string result;

		for ( size_t i = 0; i < splitted.size() - 1; ++i )
		{
			result += splitted[ i ] + "\\";
		}

		return result;
	}
	
	FileList GetFiles( const std::string& path, int depth )
	{
		FileList result;
		WIN32_FIND_DATAA findFileData;

		const std::string fullPath = path + "*.*";
		HANDLE hFind = FindFirstFileA( fullPath.c_str(), &findFileData );

		if ( hFind != INVALID_HANDLE_VALUE )
		{
			do
			{
				const std::string fileName = findFileData.cFileName;

				if ( fileName != "." && fileName != ".." )
				{
					if ( !( findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) )
					{
						const std::string itemPath = path + fileName;

						if ( !( findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
						{
							result.push_back( File( itemPath ) );
						}
						else if ( depth != 0 )
						{
							FileList children = GetFiles( itemPath, depth - 1 );
							result.insert( result.end(), children.begin(), children.end() );
						}
					}
				}

			} while ( FindNextFileA( hFind, &findFileData ) );

			FindClose( hFind );
		}

		return result;
	}

	std::string GetStartupDirectory()
	{
		CHAR buffer[ MAX_PATH + 1 ];

		::GetCurrentDirectoryA( MAX_PATH, buffer );

		return buffer;
	}
	
	std::string RelativeToAbsolutePath( const std::string& path, bool appendSlash )
	{
		return RelativeToAbsolutePath( path, GetStartupDirectory(), appendSlash );
	}
	
	std::string RelativeToAbsolutePath( const std::string& path, const std::string& base, bool appendSlash )
	{
		std::string result = path;

		// When the path is empty, we can take the startup directory:
		if ( path.empty() )
		{
			result = base;
		}
		// First we check if the path is a relative path:
		else if ( path[ 1 ] != ':' )
		{
			// Append the base path and the given path together:
			const std::string fullPath = base + '\\' + path;

			// Now we split up the paths to make sure we can remove '.' and '..' elements:
			const StringList pathList = SplitPath( fullPath );

			StringList realPathList;

			// Create the real path list:
			for ( StringList::const_iterator it = pathList.begin(); it != pathList.end(); ++it )
			{
				const std::string element = (*it);

				if ( element == ".." )
				{
					realPathList.pop_back();
				}
				else if ( element != "." )
				{
					realPathList.push_back( (*it) );
				}
			}

			result = "";
			for ( size_t i = 0; i < realPathList.size(); ++i )
			{
				if ( i != realPathList.size() - 1 || appendSlash)
				{
					result += realPathList[ i ] + "\\";
				}
				else
				{
					result += realPathList[ i ];
				}
			}
		}

		return ConvertToSystemSlashes( result );
	}

	StringList SplitPath( const std::string& path )
	{
		StringList result;

		std::string pathToParse = ConvertToSystemSlashes( path );
		std::string::size_type currPosition = pathToParse.find_first_of( '\\' );
		while ( currPosition != std::string::npos )
		{
			std::string tmpName = pathToParse.substr( 0, currPosition );
			pathToParse = pathToParse.substr( currPosition + 1 );
			currPosition = pathToParse.find_first_of( '\\' );

			if ( !tmpName.empty() )
			{
				result.push_back( tmpName );
			}
		}

		if ( !pathToParse.empty() )
		{
			result.push_back( pathToParse );
		}


		return result;
	}
}
